﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace GShare1._0.Server
{
    public class Download
    {
        public Client _client;
        public bool alive;
        public int size = 0;
        private bool overwrite = false;
        public float percent = 0;
        private Socket socket;

        public Socket Socket
        {
            get { return socket; }
            set { socket = value; }
        }
        
        private GUI_FileDownload downloadForm;
        private FileManager fileManager = new FileManager();
        private Stopwatch timer = new Stopwatch();
        System.Windows.Forms.Timer ticker = new System.Windows.Forms.Timer();


        public Download(Client client,GUI_FileDownload form)
        {
            _client = client;
            downloadForm = form;
            ticker.Tick += ticker_Tick;
            
        }

        public void BeginConnection()
        {
            Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            Socket.Bind(new IPEndPoint(0, 3334));
            Socket.Listen(0);
            Socket.BeginAccept(new AsyncCallback(AcceptCallback), null);

            
        }

        private void AcceptCallback(IAsyncResult ar)
        {
            socket.EndAccept(ar);
            DownloadStart();
        }
        void ticker_Tick(object sender, EventArgs e)
        {
            downloadForm.UpdateUI(percent, size, _client.clientInfo.UserName);
        }

        /// <summary>
        /// Starts the Download from the client
        /// Creates GUI_FileUpload
        /// </summary>
        public void DownloadStart()
        {


            alive = true;
            fileManager.CheckContainingFolder();

            //Setup Timer tick
            ticker.Interval = 1000;
            ticker.Start();
            //Debug.WriteLine(ticker.Interval.ToString());
            
            
            ServerState state = new ServerState();
            state.Client = _client._clientSocket;
            //issue first receive

            socket.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None,
            new AsyncCallback(ClientDownloadCallback), state);
            
            //Create Events for the form - Used for when closing
            downloadForm.FormClosing += downloadForm_FormClosing;

            downloadForm.ShowDialog();
            

            //Create UI for download progress
        }

        void downloadForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            //GUI_FileDownload form = (GUI_FileDownload)sender;
            this.alive = false; // Stop The Download
            this._client.autoEvent.Set(); //restart the ClientConnection Thread
        }

        public void ClientDownloadCallback(IAsyncResult ar)
        {
            try
            {
                //Update the UI
            //downloadForm.UpdateUI();
            ServerState state = (ServerState)ar.AsyncState;
            SocketError socketError;

            int dataRead = socket.EndReceive(ar, out socketError);
            int dataOffset = 0; //Simplify logic
            //Array.Resize(ref state.Buffer, dataRead);

            //check if download was canceld externaly
            if (this.alive == false)
            {
                //Debug.WriteLine("Download Stopped External");
                ticker.Stop();
                downloadForm.UpdateUI(percent, size, _client.clientInfo.UserName);
                downloadForm.CrossThreadClose();
                this._client.autoEvent.Set();
                return;
            }
            //if the socket error is found close the socket and return/exit from the async loop
            if (socketError != SocketError.Success)
            {
                Debug.WriteLine("SocketError.NOTSUCCESS");
                ticker.Stop();
                downloadForm.UpdateUI(percent, size, _client.clientInfo.UserName);
                downloadForm.CrossThreadClose();
                this._client.autoEvent.Set();
                alive = false;
                return;
            }

            
            
            if (dataRead == 9 || dataRead <= 0)
            {
                //check if still connected
                byte[] arr = state.Buffer;
                Array.Resize(ref arr, dataRead);
                if (Encoding.ASCII.GetString(arr) == "ENDUPLOAD")
                {  
                    ticker.Stop();
                    downloadForm.UpdateUI(percent, size, _client.clientInfo.UserName);
                    downloadForm.CrossThreadClose();
                    _client.autoEvent.Set();//if upload is stopped just continue with client connection thread
                    alive = false;
                    return;
                }
                
            }

            if (!state.DataSizeReceived)
            {
                if (dataRead >= 8)
                {
                    timer.Start();
                    //we received data size prefix
                    state.DataSize = BitConverter.ToInt32(state.Buffer, 0);
                    state.DataSizeReceived = true;
                    dataRead -= 8;
                    dataOffset += 8;
                    size = state.DataSize - 8;
                }
            }

            if ((state.DataReceived + dataRead) == state.DataSize)
            {
                //we have all the data
                state.DataReceived += dataRead;
                using (var stream = new FileStream(fileManager.UploadsPath + "\\" + "File.txt", FileMode.Append))
                {
                    stream.Write(state.Buffer, dataOffset, dataRead);
                }

                percent = ((float)state.DataReceived / (float)state.DataSize) * 100.0f;
                timer.Stop();
                Debug.WriteLine(timer.Elapsed.Seconds);
                Debug.WriteLine(timer.Elapsed.Minutes);
                Debug.WriteLine("Data received. Size: " + state.DataSize.ToString());
                ticker_Tick(this, new EventArgs());
                ticker.Stop();
                _client.autoEvent.Set();
                

            }
            else
            {
                //there is still data pending, store what we've
                //received and issue another BeginReceive
                //state.Data.Write(state.Buffer, dataOffset, dataRead);
                state.DataReceived += dataRead;

                //check if the file does not exist
                if (!File.Exists(fileManager.UploadsPath + "\\" + "File.txt"))
                {
                    using (var stream = new FileStream(fileManager.UploadsPath + "\\" + "File.txt", FileMode.CreateNew))
                    {
                        stream.Write(state.Buffer,dataOffset, dataRead);
                    }
                }
                //condition if the file does exists and has not started writing to the file
                if (File.Exists(fileManager.UploadsPath + "\\" + "File.txt") && !overwrite)
                {
                    using (var stream = new FileStream(fileManager.UploadsPath + "\\" + "File.txt", FileMode.Create))
                    {
                        stream.Write(state.Buffer,dataOffset, dataRead);
                    }
                    overwrite = true;
                }
                else
                {
                    using (var stream = new FileStream(fileManager.UploadsPath + "\\" + "File.txt", FileMode.Append))
                    {
                        stream.Write(state.Buffer, dataOffset, dataRead);
                    }
                }
                
                

                percent = ((float)state.DataReceived / (float)size) * 100.0f;
                //Debug.WriteLine(percent);
                if (state.DataReceived > size)
                {
                    Array.Resize(ref state.Buffer, dataRead);
                    string str = Encoding.ASCII.GetString(state.Buffer);
                    MessageBox.Show("Error ");
                    this._client.autoEvent.Set();
                    alive = false;
                    return;
                }
                //check if this is the whole file
                if (state.DataReceived == size)
                {
                    this._client.autoEvent.Set();
                    this.ticker.Stop();
                    downloadForm.UpdateUI(percent, size, _client.clientInfo.UserName);
                    alive = false;
                    return;
                }
                socket.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, new AsyncCallback(ClientDownloadCallback), state);
            }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message + " " + ex.TargetSite.ToString());
                this._client.autoEvent.Set();
                return;
            }
            
            }
        }
    }
